<template>
  <div id="three" ref="threeRef" class="map_content"></div>
</template>

<script>
import * as THREE from 'three'
import {DRACOLoader, GLTFLoader, OrbitControls} from "three/addons";
import {forEach, isFunction, size} from "lodash"
import {MODEL_EQUIPMENT_POSITION_PARAMS_ENUM} from "@/constants/ModelEquipment"
import {MODEL_SKELETON_ENUM} from "@/constants/ModelSkeleton"
import {v4 as uuid} from "uuid"
import TWEEN from "@tweenjs/tween.js"
import {CSS2DRenderer} from 'three/examples/jsm/renderers/CSS2DRenderer'

const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(
    75, // 视角
    window.innerWidth / window.innerHeight, // 宽高比
    0.1, // 近平面
    1000 // 远平面)
)
const turbine = new THREE.Group()
let modelSkeleton = null
let modelPlane = null
let modelEquipment = null
const renderer = new THREE.WebGLRenderer()
const mixers = []
const clock = new THREE.Clock()
const composers = new Map()
const renderMixins = new Map()
// const CSSRenderer = null
const controls = new OrbitControls(camera, renderer.domElement)
// 设置带阻尼
controls.enableDamping = true
// 设置阻尼系数
controls.dampingFactor = 0.05
// 设置旋转速度
// controls.autoRotate = true
camera.position.z = 5
camera.position.y = 2
camera.position.x = 2
camera.lookAt(0, 0, 0)
const equipmentList = []
const container = null

export default {
  name: 'index',
  data() {
    return {}
  },
  async mounted() {

    this.loadLights()
    await this.loadModels()
    scene.add(turbine)
    this.render()
    this.onEquipmentClick()
    this.skeletonAnimation()
    this.planeAnimation()
    this.equipmentComposeAnimation()
  },
  beforeDestroy() {
    document.removeEventListener("click", this.handler)
  },
  methods: {
    /*async initMap() {


      // const MODEL_SCALES = [0.0001 * 3, 0.0001 * 3, 0.0001 * 3]

      const renderer = new THREE.WebGLRenderer()
      renderer.setSize(window.innerWidth, window.innerHeight)
      document.body.appendChild(renderer.domElement)

      const controls = new OrbitControls(camera, renderer.domElement)
      // 设置带阻尼
      controls.enableDamping = true
      // 设置阻尼系数
      controls.dampingFactor = 0.05
      // 设置旋转速度
      // controls.autoRotate = true
      camera.position.z = 5
      camera.position.y = 2
      camera.position.x = 2
      camera.lookAt(0, 0, 0)

      /!*const axesHelper = new THREE.AxesHelper(5)
      scene.add(axesHelper)*!/

      const LIGHT_LIST = [
        [100, 100, 100],
        [-100, 100, 100],
        [100, -100, 100],
        [100, 100, -100]
      ]

      forEach(LIGHT_LIST, ([x, y, z]) => {
        const directionalLight = new THREE.DirectionalLight(0xffffff, 3)
        directionalLight.position.set(x, y, z)
        scene.add(directionalLight)
      })

      const dracoLoader = new DRACOLoader();
      dracoLoader.setDecoderPath('./draco/');
      // 实例化加载器gltf
      const gltfLoader = new GLTFLoader()
      gltfLoader.setDRACOLoader(dracoLoader);
      // 加载模型
      /!* gltfLoader.load(
           './DragonAttenuation.glb',
           (gltf) => {
             console.log(gltf)
             scene.add(gltf.scene)
           }
       )*!/

      await gltfLoader.load(
          '/models/turbine.glb',
          (gltf) => {
            console.log(gltf)
            const model = gltf.scene;
            model.position.set(0, 0, 0);
            model.scale.set(0.0001 * 3, 0.0001 * 3, 0.0001 * 3);
            modelSkeleton = model
            turbine.add(model);
            // mixer = new THREE.AnimationMixer(model);
            // mixer.clipAction(gltf.animations[0]).play();
            //
            // animate();
            // scene.add(gltf.scene)
            scene.add(turbine)
            scene.background = new THREE.Color(0xa0a0a0)
          }
      )
      await gltfLoader.load(
          '/models/plane.glb',
          (gltf) => {
            console.log(gltf)
            const model = gltf.scene;
            // model.position.set(0, 0, 0);
            model.scale.set(0.0001 * 3, 0.0001 * 3, 0.0001 * 3);
            turbine.add(model);
            scene.add(turbine)
          }
      )

      await gltfLoader.load(
          '/models/equipment.glb',
          (gltf) => {
            console.log(gltf)
            const model = gltf.scene;
            model.position.set(0, 0, 0);
            model.scale.set(0.0001 * 3, 0.0001 * 3, 0.0001 * 3);
            turbine.add(model);
            scene.add(turbine)
          }
      )

      // 风机骨架消隐动画
      function skeletonAnimation() {
        console.log(11111, modelSkeleton)
        const shellModel = modelSkeleton.getObjectByName(
            MODEL_SKELETON_ENUM.ColorMaterial
        )
        const clippingPlane = new THREE.Plane(new THREE.Vector3(0, -1, 0), 3.5)
        shellModel?.traverse((mesh) => {
          if (!(mesh instanceof THREE.Mesh)) return undefined
          mesh.material = new THREE.MeshPhysicalMaterial({
            color: 0xffffff,
            metalness: 1,
            roughness: 0.7
          })
          // 白色外壳消隐效果
          mesh.material.clippingPlanes = [clippingPlane]
          return undefined
        })
        const uid = uuid()
        renderMixins.set(uid, () => {
          if (clippingPlane.constant <= -0.1) {
            modelSkeleton.value?.remove(shellModel)
            renderMixins.delete(uid)
            console.log("renderMixins", renderMixins)
          }
          clippingPlane.constant -= 0.01
        })
      }

      skeletonAnimation()

      function animate() {
        controls.update()
        requestAnimationFrame(animate)
        // const delta = clock.getDelta();

        // mixer.update(delta);

        // controls.update();

        // stats.update();
        // 更新tween
        // tween.update()
        // 渲染
        renderer.render(scene, camera)
      }

      animate()
    },*/
    loadLights() {
      const LIGHT_LIST = [
        [100, 100, 100],
        [-100, 100, 100],
        [100, -100, 100],
        [100, 100, -100]
      ]

      forEach(LIGHT_LIST, ([x, y, z]) => {
        const directionalLight = new THREE.DirectionalLight(0xffffff, 3)
        directionalLight.position.set(x, y, z)
        scene.add(directionalLight)
      })
    },
    async loadModels() {
      await this.loadTurbineSkeleton()
      await this.loadTurbinePlane()
      await this.loadTurbineEquipments()
    },
    loadAnimate(
        mesh,
        animations,
        animationName
    ) {
      const mixer = new THREE.AnimationMixer(mesh)
      const clip = THREE.AnimationClip.findByName(animations, animationName)
      if (!clip) return undefined
      const action = mixer.clipAction(clip)
      action.play()
      mixers.push(mixer)
      return undefined
    },
    // 加载风机骨架
    async loadTurbineSkeleton() {
      const gltf = await this.loadGLTF('/models/turbine.glb')
      const model = gltf.scene
      model.position.set(0, 0, 0);
      model.scale.set(0.0001 * 3, 0.0001 * 3, 0.0001 * 3);
      this.loadAnimate(model, gltf.animations, gltf.animations[0].name)
      model.name = 'skeleton'
      modelSkeleton = model
      turbine.add(model);
      scene.add(turbine)
    },
    // 加载风机平台
    async loadTurbinePlane() {
      const gltf = await this.loadGLTF('/models/plane.glb')
      const model = gltf.scene
      model.position.set(0, 0, 0);
      model.scale.set(0.0001 * 3, 0.0001 * 3, 0.0001 * 3);
      model.name = 'plane'
      modelPlane = model
      turbine.add(model);
      scene.add(turbine)
    },
    // 加载风机设备
    async loadTurbineEquipments() {
      const gltf = await this.loadGLTF('/models/equipment.glb')
      const model = gltf.scene
      // model.position.set(0, 0, 0);
      model.scale.set(0.0001 * 3, 0.0001 * 3, 0.0001 * 3);
      model.name = 'equipment'
      modelEquipment = model
      turbine.add(model);
      scene.add(turbine)
    },
    async loadGLTF(url) {
      const dracoLoader = new DRACOLoader();
      dracoLoader.setDecoderPath('./draco/');
      // 实例化加载器gltf
      const gltfLoader = new GLTFLoader()
      gltfLoader.setDRACOLoader(dracoLoader);
      const onCompleted = (object, resolve) => resolve(object)
      return new Promise((resolve) => {
        gltfLoader.load(url, (object) => onCompleted(object, resolve))
      })
    },
    render() {
      renderer.setSize(window.innerWidth, window.innerHeight)
      document.body.appendChild(renderer.domElement)
      const delta = new THREE.Clock().getDelta()
      renderer.render(scene, camera)
      controls.update()
      const mixerUpdateDelta = clock.getDelta()
      mixers.forEach((mixer) => mixer.update(mixerUpdateDelta))
      composers.forEach((composer) => composer.render(delta))
      renderMixins.forEach((mixin) => isFunction(mixin) && mixin())
      // renderer.render(scene, camera)
      const CSSRenderer = new CSS2DRenderer()
      CSSRenderer.render(scene, camera)
      TWEEN.update()
      requestAnimationFrame(() => this.render())
    },
    // 风机设备点击事件
    onEquipmentClick() {
      console.log(88888, modelEquipment)
      modelEquipment.traverse((mesh) => {
        console.log(66666, mesh)
        if (!(mesh instanceof THREE.Mesh)) return undefined
        const {material} = mesh
        mesh.material = material.clone()
        equipmentList.push(mesh)
        return undefined
      })
      document.addEventListener("click", this.handler)
    },
    handler(event) {
      console.log(666)
      const el = container
      console.log(555, el)
      const mouse = new THREE.Vector2(
          (event.clientX / el.offsetWidth) * 2 - 1,
          -(event.clientY / el.offsetHeight) * 2 + 1
      )
      const raycaster = new THREE.Raycaster()
      raycaster.setFromCamera(mouse, camera)
      const intersects = raycaster.intersectObject(modelEquipment, true)
      // console.log('intersects', intersects[0].point)
      if (size(intersects) <= 0) return undefined
      const equipment = intersects[0].object
      if (!equipment) return undefined
      equipmentList.forEach((child) => {
        child.material.emissive.setHex(child.currentHex)
      })
      equipment.currentHex =
          equipment.currentHex ?? equipment.material.emissive.getHex()
      equipment.material.emissive.setHex(0xff0000)
      return undefined
    },
    // 风机骨架消隐动画
    skeletonAnimation() {
      const shellModel = modelSkeleton.getObjectByName(
          MODEL_SKELETON_ENUM.ColorMaterial
      )
      const clippingPlane = new THREE.Plane(new THREE.Vector3(0, -1, 0), 3.5)
      shellModel?.traverse((mesh) => {
        if (!(mesh instanceof THREE.Mesh)) return undefined
        mesh.material = new THREE.MeshPhysicalMaterial({
          color: 0xffffff,
          metalness: 1,
          roughness: 0.7
        })
        // 白色外壳消隐效果
        mesh.material.clippingPlanes = [clippingPlane]
        return undefined
      })
      const uid = uuid()
      renderMixins.set(uid, () => {
        if (clippingPlane.constant <= -0.1) {
          modelSkeleton.remove(shellModel)
          renderMixins.delete(uid)
          console.log("renderMixins", renderMixins)
        }
        clippingPlane.constant -= 0.01
      })
    },
    // 风机平台动画
    planeAnimation() {
      const texture = modelPlane.children[0].material.map
      texture.wrapS = THREE.RepeatWrapping
      texture.wrapT = THREE.RepeatWrapping
      const uid = uuid()
      renderMixins.set(uid, () => {
        const count = texture.repeat.y
        if (count <= 10) {
          texture.repeat.x += 0.01
          texture.repeat.y += 0.02
        } else {
          texture.repeat.x = 0
          texture.repeat.y = 0
        }
      })
    },
    animation(props) {
      const {
        from,
        to,
        duration,
        easing = TWEEN.Easing.Quadratic.Out,
        onUpdate,
        onComplete,
      } = props
      return new TWEEN.Tween(from)
          .to(to, duration)
          .easing(easing)
          .onUpdate((object) => isFunction(onUpdate) && onUpdate(object))
          .onComplete((object) => isFunction(onComplete) && onComplete(object))
          .start()
    },
    // 设备合成动画
    async equipmentComposeAnimation() {
      this.groundAndSkeletonShowAnimation()
      modelEquipment.children.forEach((child) => {
        const params = MODEL_EQUIPMENT_POSITION_PARAMS_ENUM[child.name]
        this.animation({
          from: child.position,
          to: params.COMPOSE,
          duration: 2 * 1000,
          onUpdate: (position) => {
            child.position.set(position.x, position.y, position.z)
          }
        })
      })
    },
    // 设备分解动画
    async equipmentDecomposeAnimation() {
      this.groundAndSkeletonHideAnimation()
      // await sleep(1 * 1000);
      modelEquipment.value?.updateMatrixWorld()
      modelEquipment.value?.children.forEach((child) => {
        const params = MODEL_EQUIPMENT_POSITION_PARAMS_ENUM[child.name]
        this.animation({
          from: child.position,
          to: params.DECOMPOSE,
          duration: 2 * 1000,
          onUpdate: (position) => {
            child.position.set(position.x, position.y, position.z)
          }
        })
      })
    },
    // 地面和风机骨架显示
    groundAndSkeletonShowAnimation() {
      const clippingPlane = new THREE.Plane(new THREE.Vector3(0, -1, 0), -0.1)
      modelSkeleton.traverse((mesh) => {
        if (!(mesh instanceof THREE.Mesh)) return undefined
        mesh.material.clippingPlanes = [clippingPlane]
        return undefined
      })
      modelPlane.traverse((mesh) => {
        if (!(mesh instanceof THREE.Mesh)) return undefined
        mesh.material.clippingPlanes = [clippingPlane]
        return undefined
      })
      const uid = uuid()
      renderMixins.set(uid, () => {
        if (clippingPlane.constant >= 3.5) renderMixins.delete(uid)
        clippingPlane.constant += 0.04
      })
    },
    // 地面和风机骨架消隐藏
    groundAndSkeletonHideAnimation() {
      const clippingPlane = new THREE.Plane(new THREE.Vector3(0, -1, 0), 3.5)
      modelSkeleton.traverse((mesh) => {
        if (!(mesh instanceof THREE.Mesh)) return undefined
        mesh.material.clippingPlanes = [clippingPlane]
        return undefined
      })
      modelPlane.traverse((mesh) => {
        if (!(mesh instanceof THREE.Mesh)) return undefined
        mesh.material.clippingPlanes = [clippingPlane]
        return undefined
      })
      const uid = uuid()
      renderMixins.set(uid, () => {
        if (clippingPlane.constant <= -0.1) renderMixins.delete(uid)
        clippingPlane.constant -= 0.04
      })
    }
  }
}
</script>

<style scoped lang="less">

</style>
